package com.purvar.mybatis.util.mapper;

import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.purvar.exception.BusinessException;
import com.purvar.mybatis.util.bean.InverseJoinColumnen;
import com.purvar.mybatis.util.bean.JoinColumnen;
import com.purvar.mybatis.util.bean.Relation;
import com.purvar.mybatis.util.enums.FieldCollectionType;
import com.purvar.util.BeanCopierUtil;
import lombok.Data;
import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.ObjectFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.*;
import java.util.stream.Collectors;

/**
 * AbstractAutoMapper for one2one/one2many/many2many
 *
 * @author zcc
 */
@Data
public abstract class AbstractAutoMapper {
    @Autowired
    ObjectFactory<SqlSession> factory;


    private Map<String, List<Relation>> relationListMap = new HashMap<>();

    public <T, E> void relationCommon(List<T> tList, Relation entity, QueryWrapper qW) {
        List<JoinColumnen> joinColumnens = entity.getJoinColumnens();
        // Class<E> entityClass2 = (Class<E>) fc.getFieldClass();
        Class<?> mapperClass = entity.getMapper();
        // BaseMapper<E> mapper = (BaseMapper<E>) getMapperBean(mapperClass);
        BaseMapper<E> mapper = (BaseMapper<E>) factory.getObject().getMapper(mapperClass);
        QueryWrapper queryWrapper = qW == null ? new QueryWrapper() : qW;
        joinColumnens.stream().forEach(i -> {
            //获取list  集合的所有这个数据
            List<Object> objects = tList.stream().map(k -> {
                try {
                    Field columnField = k.getClass().getDeclaredField(camelCaseName(i.getName()));
                    columnField.setAccessible(true);
                    return (Serializable) columnField.get(k);
                } catch (Exception e) {
                    throw new BusinessException("refProperty/refPropertyValue one to many id is not correct!");
                }
            }).collect(Collectors.toList());
            if (StringUtils.isEmpty(i.getReferencedColumnName())) {
                i.setReferencedColumnName(HumpToUnderline(entity.getTableId()));

            }
            queryWrapper.in(i.getReferencedColumnName(), objects);
        });
        List<E> list = mapper.selectList(queryWrapper);
        //将值设置进入属性
        setValueToVo(entity, tList, list);
    }

    public <T, E> void oneToMany(List<T> tList, Relation entity, QueryWrapper queryWrapper) {
        relationCommon(tList, entity, queryWrapper);
    }

    public <T, E> void setValueToVo(Relation entity, List<T> t, List<E> e) {
        t.stream().forEach(i -> {
            List<E> es = e.stream().filter(k -> {
                boolean ret = false;
                int num = 0;
                for (JoinColumnen joinColumnen : entity.getJoinColumnens()) {
                    try {
                        Field field = i.getClass().getDeclaredField(camelCaseName(joinColumnen.getName()));
                        Field field2 = k.getClass().getDeclaredField(camelCaseName(joinColumnen.getReferencedColumnName()));
                        field.setAccessible(true);
                        field2.setAccessible(true);
                        if (field.getType() == String.class) {
                            if (field.get(i).equals(field2.get(k))) {
                                num++;
                            }
                        } else {
                            if (field.get(i) == field2.get(k)) {
                                num++;
                            }
                        }
                        field.setAccessible(false);
                        field2.setAccessible(false);
                    } catch (NoSuchFieldException ex) {
                        ex.printStackTrace();
                    } catch (IllegalAccessException ex) {
                        ex.printStackTrace();
                    }
                }
                return num == entity.getJoinColumnens().size();
            }).collect(Collectors.toList());
            Field field = null;
            try {
                field = i.getClass().getDeclaredField(entity.getFiledName());
                field.setAccessible(true);
                // list to set
                if (entity.getFieldCollectionType() == FieldCollectionType.SET) {
                    if (entity.getMapperClazz() != entity.getEntityClazz()) {
                        field.set(i, BeanCopierUtil.copyListToSet(es, entity.getEntityClazz()));
                    } else {
                        Set<E> set = new HashSet<E>();
                        for (E e1 : es) {// list 被访问，导致延迟立即加载，延迟失败！
                            set.add(e1);
                        }
                        field.set(i, set);
                    }
                } else if (entity.getFieldCollectionType() == FieldCollectionType.LIST) {
                    if (entity.getMapperClazz() != entity.getEntityClazz()) {
                        field.set(i, BeanCopierUtil.copyListToList(es, entity.getEntityClazz()));
                    } else {
                        field.set(i, es);
                    }
                } else {
                    if (es.size() > 0) {
                        if (entity.getMapperClazz() != entity.getEntityClazz()) {
                            field.set(i, BeanCopierUtil.copyObject(es.get(0), entity.getEntityClazz()));
                        } else {
                            field.set(i, es.get(0));
                        }

                    }
                }
                field.setAccessible(false);
            } catch (Exception e2) {
                throw new RuntimeException(
                        String.format("{0} call setter {1} is not correct!", entity, field.getName()));
            }

        });

    }


    public String HumpToUnderline(String para) {
        StringBuilder sb = new StringBuilder(para);
        int temp = 0;
        if (!para.contains("_")) {
            for (int i = 0; i < para.length(); i++) {
                if (Character.isUpperCase(para.charAt(i))) {
                    sb.insert(i + temp, "_");
                    temp += 1;
                }
            }
        }
        return sb.toString().toLowerCase();
    }

    public String camelCaseName(String underscoreName) {
        StringBuilder result = new StringBuilder();
        if (underscoreName != null && underscoreName.length() > 0) {
            boolean flag = false;
            for (int i = 0; i < underscoreName.length(); i++) {
                char ch = underscoreName.charAt(i);
                if ("_".charAt(0) == ch) {
                    flag = true;
                } else {
                    if (flag) {
                        result.append(Character.toUpperCase(ch));
                        flag = false;
                    } else {
                        result.append(ch);
                    }
                }
            }
        }
        return result.toString();
    }

    public <T> void oneToMany(T t, Relation entity, QueryWrapper queryWrapper) {
        List<T> ts = new ArrayList<>();
        ts.add(t);
        this.oneToMany(ts, entity, queryWrapper);
    }

    public <T> void ManyToOne(List<T> tList, Relation entity, QueryWrapper queryWrapper) {
        relationCommon(tList, entity, queryWrapper);
    }

    public <T> void ManyToOne(T t, Relation entity, QueryWrapper queryWrapper) {
        List<T> ts = new ArrayList<>();
        ts.add(t);
        this.ManyToOne(ts, entity, queryWrapper);
    }

    public <T> void oneToOne(List<T> tList, Relation entity, QueryWrapper queryWrapper) {
        relationCommon(tList, entity, queryWrapper);
    }

    public <T> void oneToOne(T t, Relation entity, QueryWrapper queryWrapper) {
        List<T> ts = new ArrayList<>();
        ts.add(t);
        this.oneToOne(ts, entity, queryWrapper);
    }

    public <T, M, E> void manyToMany(List<T> tList, Relation entity, QueryWrapper qw) {
        //取出所有关联表的数据
        List<JoinColumnen> joinColumnens = entity.getJoinColumnens();
        // Class<E> entityClass2 = (Class<E>) fc.getFieldClass();
        Class<?> mapperClass = entity.getJoinTableEn().getRelationMapper();
        // BaseMapper<E> mapper = (BaseMapper<E>) getMapperBean(mapperClass);
        BaseMapper<M> mapper = (BaseMapper<M>) factory.getObject().getMapper(mapperClass);
        QueryWrapper queryWrapper = new QueryWrapper();
        joinColumnens.stream().forEach(i -> {
            //获取list  集合的所有这个数据
            List<Object> objects = tList.stream().map(k -> {
                try {
                    Field columnField = k.getClass().getDeclaredField(i.getName());
                    columnField.setAccessible(true);
                    return (Serializable) columnField.get(k);
                } catch (Exception e) {
                    throw new BusinessException("refProperty/refPropertyValue one to many id is not correct!");
                }
            }).collect(Collectors.toList());
            if (StringUtils.isEmpty(i.getReferencedColumnName())) {
                i.setReferencedColumnName(HumpToUnderline(entity.getTableId()));

            }
            queryWrapper.in(i.getReferencedColumnName(), objects);
        });
        List<M> listM = mapper.selectList(queryWrapper);
        if (CollectionUtil.isEmpty(listM)) {
            return;
        }
        QueryWrapper queryWrapperM = qw == null ? new QueryWrapper() : qw;
        //取关联的子表数据
        List<InverseJoinColumnen> inverseJoinColumnens = entity.getInverseJoinColumns();
        inverseJoinColumnens.stream().forEach(i -> {
            //获取list  集合的所有这个数据
            List<Object> objects = listM.stream().map(k -> {
                try {
                    Field columnField = k.getClass().getDeclaredField(camelCaseName(i.getName()));
                    columnField.setAccessible(true);
                    return (Serializable) columnField.get(k);
                } catch (Exception e) {
                    throw new BusinessException("refProperty/refPropertyValue one to many id is not correct!");
                }
            }).collect(Collectors.toList());
            if (StringUtils.isEmpty(i.getReferencedColumnName())) {
                i.setReferencedColumnName(HumpToUnderline(entity.getTableId()));
            }
            queryWrapperM.in(i.getReferencedColumnName(), objects);
        });

        Class<?> mapperClassE = entity.getMapper();
        // BaseMapper<E> mapper = (BaseMapper<E>) getMapperBean(mapperClass);
        BaseMapper<M> mapperE = (BaseMapper<M>) factory.getObject().getMapper(mapperClassE);
        List<E> es = mapperE.selectList(queryWrapperM);
        setValueToVoManyToMany(entity, tList, listM, es);

    }

    public <T, M, E> void setValueToVoManyToMany(Relation entity, List<T> t, List<M> m, List<E> e) {
        t.stream().forEach(i -> {
            List<M> ms = m.stream().filter(k -> {
                boolean ret = false;
                int num = 0;
                for (JoinColumnen joinColumnen : entity.getJoinColumnens()) {
                    try {
                        Field field = i.getClass().getDeclaredField(camelCaseName(joinColumnen.getName()));
                        Field field2 = k.getClass().getDeclaredField(camelCaseName(joinColumnen.getReferencedColumnName()));
                        field.setAccessible(true);
                        field2.setAccessible(true);
                        if (field.getType() == String.class) {
                            if (field.get(i).equals(field2.get(k))) {
                                num++;
                            }
                        } else {
                            if (field.get(i) == field2.get(k)) {
                                num++;
                            }
                        }
                        field.setAccessible(false);
                        field2.setAccessible(false);
                    } catch (NoSuchFieldException ex) {
                        ex.printStackTrace();
                    } catch (IllegalAccessException ex) {
                        ex.printStackTrace();
                    }
                }
                return num == entity.getJoinColumnens().size();
            }).collect(Collectors.toList());

            //取listE
            List<E> es = e.stream().filter(k -> {
                boolean ret = false;
                for (M j : ms) {
                    for (InverseJoinColumnen joinColumnen : entity.getInverseJoinColumns()) {
                        try {
                            Field field = j.getClass().getDeclaredField(camelCaseName(joinColumnen.getName()));
                            Field field2 = k.getClass().getDeclaredField(camelCaseName(joinColumnen.getReferencedColumnName()));
                            field.setAccessible(true);
                            field2.setAccessible(true);
                            if (field.getType() == String.class) {
                                if (field.get(j).equals(field2.get(k))) {
                                    ret = true;
                                    continue;
                                }
                            } else {
                                if (field.get(i) == field2.get(k)) {
                                    ret = true;
                                    continue;
                                }
                            }
                            field.setAccessible(false);
                            field2.setAccessible(false);
                        } catch (NoSuchFieldException ex) {
                            ex.printStackTrace();
                        } catch (IllegalAccessException ex) {
                            ex.printStackTrace();
                        }
                    }
                }

                return ret;


            }).collect(Collectors.toList());


            Field field = null;
            try {
                field = i.getClass().getDeclaredField(entity.getFiledName());
                field.setAccessible(true);
                // list to set
                if (entity.getFieldCollectionType() == FieldCollectionType.SET) {
                    if (entity.getMapperClazz() != entity.getEntityClazz()) {
                        field.set(i, BeanCopierUtil.copyListToSet(es, entity.getEntityClazz()));
                    } else {
                        Set<E> set = new HashSet<E>();
                        for (E e1 : es) {// list 被访问，导致延迟立即加载，延迟失败！
                            set.add(e1);
                        }
                        field.set(i, set);
                    }
                } else if (entity.getFieldCollectionType() == FieldCollectionType.LIST) {
                    if (entity.getMapperClazz() != entity.getEntityClazz()) {
                        field.set(i, BeanCopierUtil.copyListToList(es, entity.getEntityClazz()));
                    } else {
                        field.set(i, es);
                    }
                } else {
                    if (es.size() > 0) {
                        if (entity.getMapperClazz() != entity.getEntityClazz()) {
                            field.set(i, BeanCopierUtil.copyObject(es.get(0), entity.getEntityClazz()));
                        } else {
                            field.set(i, es.get(0));
                        }

                    }
                }
                field.setAccessible(false);
            } catch (Exception e2) {
                throw new RuntimeException(
                        String.format("{0} call setter {1} is not correct!", entity, field.getName()));
            }
        });
    }

    public <T> void manyToMany(T t, Relation entity, QueryWrapper queryWrapper) {
        List<T> ts = new ArrayList<>();
        ts.add(t);
        this.manyToMany(ts, entity, queryWrapper);
    }


}
